Python prompts [Errno 32] for Broken pipe to cause the thread crash error resolution

  • 2020-04-02 14:21:03
  • OfStack

This article illustrates an error resolution for a Python prompt [Errno 32] that causes a thread crash. Share with you for your reference. Specific methods are as follows:

1. Error phenomena
The HTTP service implemented by ThreadingHTTPServer, if the client actively disconnects before the server returns, the server will report an error [Errno 32] Broken pipe and cause the processing thread to crash.
Let's start with an example,python version: 2.7
The sample code

#!/usr/bin/env python
#!coding=utf-8
 
import os
import time
import socket
import threading
from BaseHTTPServer import HTTPServer ,BaseHTTPRequestHandler
from SocketServer import ThreadingMixIn
 
class RequestHandler(BaseHTTPRequestHandler):
    def do_GET(self):
        """
        To deal with get request
        """      
        query=self.path
        print "query: %s thread=%s" % (query, str(threading.current_thread()))
        
        #ret_str="<html>" + self.path + "<br>" + str(self.server) + "<br>" + str(self.responses) +  "</html>"
        ret_str="<html>" + self.path + "<br>" + str(self.server) +  "</html>"
        
        time.sleep(5)
        
        try:
            self.send_response(200)
            self.send_header('Content-type','text/html')
            self.end_headers()
            self.wfile.write(ret_str)
        except socket.error, e:
            print "socket.error : Connection broke. Aborting" + str(e)
            self.wfile._sock.close()  # close socket
            self.wfile._sock=None
            return False
       
        print "success prod query :%s" % (query)
        return True
 
# multithreading
class ThreadingHTTPServer(ThreadingMixIn,HTTPServer):
    pass
    
if __name__ == '__main__':
    serveraddr = ('',9001)
 
    ser = ThreadingHTTPServer(serveraddr,RequestHandler)
    ser.serve_forever()
    sys.exit(0)

Run the service
. / thread_http_server_error. Py
Curl for the first time, waiting to return
[~]$curl -s 'http://10.232.41.142:9001/hello1 ' 
<html>/hello1<br><__main__.ThreadingHTTPServer instance at 0x37483b0></html>[~]$
At this point, the server-side output log is as follows:
$./thread_http_server_error.py
query: /hello1 thread=
search041142.sqa.cm4.tbsite.net � - [15/May/2014 15:02:27] " GET /hello1 HTTP/1.1 " 200 -
success prod query :/hello1

  Curl the second time, without waiting to return, CTRL +C to simulate client disconnection
[~]$curl -s 'http://10.232.41.142:9001/hello2 ' 
[~]$ ctrl+C

At this point, the server-side output log is as follows:
query: /hello2 thread=
search041142.sqa.cm4.tbsite.net � - [15/May/2014 15:33:10] " GET /hello2 HTTP/1.1 " 200 -
socket.error : Connection broke. Aborting[Errno 32] Broken pipe
-- -- -- -- -- -- - -
Exception happened during processing of request from ('10.232.41.142 ' , 48769)
Traceback (most recent call last):
File " /home/wuzhu/tools/python_2_7_1/lib/python2.7/SocketServer.py " , line 582, in process_request_thread
self.finish_request(request, client_address)
File " /home/wuzhu/tools/python_2_7_1/lib/python2.7/SocketServer.py " , line 323, in finish_request
self.RequestHandlerClass(request, client_address, self)
File " /home/wuzhu/tools/python_2_7_1/lib/python2.7/SocketServer.py " , line 639, in __init__
self.handle()
File " /home/wuzhu/tools/python_2_7_1/lib/python2.7/BaseHTTPServer.py " , line 337, in handle
self.handle_one_request()
File " /home/wuzhu/tools/python_2_7_1/lib/python2.7/BaseHTTPServer.py " , line 326, in handle_one_request
 self.wfile.flush() #actually send the response if not already done.
File " /home/wuzhu/tools/python_2_7_1/lib/python2.7/socket.py " , line 303, in flush
self._sock.sendall(view[write_offset:write_offset+buffer_size])
AttributeError: 'NoneType' object has no attribute 'sendall'

2. Cause analysis

The reason for "[Errno 32] Broken pipe" is quite clear. Since the client actively disconnected the server before it returned, the server received SIGPIPE error when it wrote socket. Although we also handled the exception in our program by closing the handler's wfile._sock object, the member function handle_one_request of the BaseHTTPRequestHandler class in the python library basehttpserver.py calls wfile.flush directly without determining whether the wfile is close.

def handle_one_request(self):
    """Handle a single HTTP request.
 
    You normally don't need to override this method; see the class
    __doc__ string for information on how to handle specific HTTP
    commands such as GET and POST.
 
    """
    try:
        self.raw_requestline = self.rfile.readline(65537)
        if len(self.raw_requestline) > 65536:
            self.requestline = ''
            self.request_version = ''
            self.command = ''
            self.send_error(414)
            return
        if not self.raw_requestline:
            self.close_connection = 1
            return
        if not self.parse_request():
            # An error code has been sent, just exit
            return
        mname = 'do_' + self.command
        if not hasattr(self, mname):
            self.send_error(501, "Unsupported method (%r)" % self.command)
            return
        method = getattr(self, mname)
        method()
        # No judgment wfile Whether they have been close Just call directly flush()
        self.wfile.flush() #actually send the response if not already done.
    except socket.timeout, e:
        #a read or a write timed out.  Discard this connection
        self.log_error("Request timed out: %r", e)
        self.close_connection = 1
        return

3. Solutions

Simply override the member function handle_one_reques () of RequestHandler's base class BaseHTTPRequestHandler and add whether the wfile is close before calling wfile.flush().

#!/usr/bin/env python
#!coding=utf-8 import os
import time
import socket
import threading
from BaseHTTPServer import HTTPServer ,BaseHTTPRequestHandler
from SocketServer import ThreadingMixIn class RequestHandler(BaseHTTPRequestHandler):
    
    def handle_one_request(self):
        """Handle a single HTTP request.
 
        You normally don't need to override this method; see the class
        __doc__ string for information on how to handle specific HTTP
        commands such as GET and POST.
 
        """
        try:
            self.raw_requestline = self.rfile.readline(65537)
            if len(self.raw_requestline) > 65536:
                self.requestline = ''
                self.request_version = ''
                self.command = ''
                self.send_error(414)
                return
            if not self.raw_requestline:
                self.close_connection = 1
                return
            if not self.parse_request():
                # An error code has been sent, just exit
                return
            mname = 'do_' + self.command
            if not hasattr(self, mname):
                self.send_error(501, "Unsupported method (%r)" % self.command)
                return
            method = getattr(self, mname)
            print "before call do_Get"
            method()
            # increase debug info and wfile To determine whether or not close
            print "after call do_Get"
            if not self.wfile.closed:
                self.wfile.flush() #actually send the response if not already done.
            print "after wfile.flush()"
        except socket.timeout, e:
            #a read or a write timed out.  Discard this connection
            self.log_error("Request timed out: %r", e)
            self.close_connection = 1
            return
    
    def do_GET(self):
        """
        To deal with get request
        """
        query=self.path
        print "query: %s thread=%s" % (query, str(threading.current_thread()))
 
        ret_str="<html>" + self.path + "<br>" + str(self.server) +  "</html>"
 
        time.sleep(5)
        
        try:
            self.send_response(200)
            self.send_header('Content-type','text/html')
            self.end_headers()         
            self.wfile.write(ret_str)
        except socket.error, e:
            print "socket.error : Connection broke. Aborting" + str(e)
            self.wfile._sock.close()
            self.wfile._sock=None
            return False
       
        print "success prod query :%s" % (query)
        return True
 
# multithreading
class ThreadingHTTPServer(ThreadingMixIn,HTTPServer):
    pass
    
if __name__ == '__main__':
    serveraddr = ('',9001)
 
    ser = ThreadingHTTPServer(serveraddr,RequestHandler)
    ser.serve_forever()
    sys.exit(0)

Run the service
. / thread_http_server. Py
Curl, without waiting to return, CTRL +C to simulate client disconnection
[~]$curl -s 'http://10.232.41.142:9001/hello2'
[~]$ ctrl+C

At this point, the server-side output log is as follows:
$./thread_http_server.pybefore call do_Get
query: /hello2 thread=<Thread(Thread-1, started 1103210816)>
search041142.sqa.cm4.tbsite.net - - [15/May/2014 15:54:09] "GET /hello2 HTTP/1.1" 200 -
socket.error : Connection broke. Aborting[Errno 32] Broken pipe
after call do_Get
after wfile.flush()

I hope this article has helped you with your Python programming.


Related articles: